package com.datasecops.scannerservices.service;

import com.datasecops.scannerservices.config.ConfigFilePath;
import com.datasecops.scannerservices.entity.CommonParam;
import com.datasecops.scannerservices.entity.ScanCondition;
import com.datasecops.scannerservices.enumerate.EngineStatus;
import com.datasecops.scannerservices.util.AESEncoder;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.*;

/**共享扫描流程服务
 * @author: GeXiaoLong
 * @time: 2023/3/29 15:49
 */
@Service
@Slf4j
public class ShareScanProcessServer {

    @Resource
    private ScannerEngineServer engineServer;



    public void startShareScanProcess(Document document, CommonParam commonParam, String scanJobId, String jobName,
                                      String scannerID){
        Element rootElement = document.getRootElement();
        Element sqlScannerElem;
        //扫描任务Id
        String jobId = rootElement.attributeValue("JobID");//取出JobID的值

        sqlScannerElem = rootElement.element("FileSystemScanner");//取得根节点下FileSystemScanner字节点


        if (null == sqlScannerElem) {
            log.error("Failed Method scanProcess sqlScannerDom is null");
            return;
        }

        ScanCondition scanCondition = new ScanCondition();

        //扫描条件
        Element advanceCtlElem = sqlScannerElem.element("AdvanceCtl");
        String advanceCtlStatus = advanceCtlElem.attributeValue("Status");

        //过滤器
        Element pathFilterElem = sqlScannerElem.element("PathFilter");
        String pathFilterStatus = pathFilterElem.attributeValue("Status");
        if (pathFilterStatus.equalsIgnoreCase("Enable")){
            List<Element> elements = pathFilterElem.elements();
            for (Element e : elements) {
                String type = e.attributeValue("Type");
                String param = e.attributeValue("Parameter");
                if (type.equalsIgnoreCase("Include")){

                    scanCondition.setIncludeFilter(param);
                }else if(type.equalsIgnoreCase("Exclude")){
                    scanCondition.setExcludeFilter(param);
                }
            }
        }

        //一条扫描任务对应一个GUID
        String uuId = UUID.randomUUID().toString().toUpperCase();

        //扫描目标 --重点
        Element scanContentElem = sqlScannerElem.element("ScanContent");
        if (null == scanContentElem) {
            log.error("Failed Method scanProcess scanContentDom is null");
            return;
        }
        //>>>>>>>>>>1、调用扫描引擎--执行开始命令 ScannerEngine.exe
        engineServer.callScannerEngine(jobId, "scanproxy", EngineStatus.START.getValue(), null);

        //>>>>>>>>>>2、批次扫描数据库
        List<Element> scanElements = scanContentElem.elements();
        List<Element> privateList3 = scanContentElem.elements("Default");
        String name = null ;
        String path = null;
        String password = null;
        String paths = null;
        for (Element element : privateList3) {
            Element e1 = element.element("Credential");
            if (e1 == null){
                continue;
            }
             password = e1.attributeValue("PasswordAES");
            name = e1.attributeValue("UserName");
             path = e1.attributeValue("Parameter");
            paths = e1.attributeValue("Password");
            System.out.println(password);

            password = AESEncoder.DecryptV2WithKey(password);

        }


        List<Element> privateList2 = scanContentElem.elements("PrivateList");
        for (Element element : privateList2) {
            Element e1 = element.element("Credential");
            if (e1 == null){
                continue;
            }
             password = e1.attributeValue("PasswordAES");
             name = e1.attributeValue("UserName");
             path = e1.attributeValue("Parameter");
            System.out.println(password);

            password = AESEncoder.DecryptV2WithKey(password);

            //网络共享
            //networkSharingScanning(uuId,jobId,password,name,path,scanJobId,jobName,scannerID,commonParam);

        }

        networkSharingScanning(uuId,jobId,password,name,path,scanJobId,jobName,scannerID,commonParam);





        //>>>>>>>>>>3、调用扫描引擎--执行结束命令
        engineServer.callScannerEngine(jobId, "scanproxy", EngineStatus.STOP.getValue(), null);
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>SCANNER END>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    }


     /**
      *网络共享落盘
      * @Description
      * @author GeXiaoLong
      * @time 2023/3/30 15:28
      * @param
      * @param commonParam
      * @return
      */
    private void networkSharingScanning(String uuId, String jobId, String password, String name, String path,
                                        String scanJobId, String jobName, String scannerID, CommonParam commonParam){
     /*   String sharePath = "smb://10.26.18.4/incoming/测试数据/";
        String localPath = "P://text//souls//";*/
        //基础path
        StringBuilder basicPath = new StringBuilder(ConfigFilePath.getScannerDirPath() + File.separator + jobId + File.separator +uuId);
        //暂存目录
        StringBuilder filePaths = new StringBuilder(basicPath);
        filePaths.append(File.separator).append("FileServer").append(File.separator);
        File localDirs = new File(String.valueOf(filePaths));
        if (!localDirs.exists()) {
            localDirs.mkdirs();
        }
        String paths = path.replace("\\\\", "//").replace("\\", "/");
        String sharePath = "smb:"+paths+"/";
        String localPath = String.valueOf(filePaths);
        NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("", name, password);
        SmbFile smbFile;
        try {
            smbFile = new SmbFile(sharePath, auth);
            if (smbFile.isDirectory()) {
                // 获取目录下的文件和子目录
                SmbFile[] files = smbFile.listFiles();
                for (SmbFile file : files) {
                    //String fileName = file.getName().replace("/","");
                    String encodedFileName = URLEncoder.encode(file.getName(), "UTF-8");
                    String fileName = URLDecoder.decode(encodedFileName, "UTF-8").replace("/", "");
                    String filePath = localPath + fileName;
                    log.error("路径乱码的源文件是: " + fileName);
                    // 如果是子目录，递归下载子目录中的文件和子目录
                    if (file.isDirectory()) {
                        File localDir = new File(filePath);
                        if (!localDir.exists()) {
                            localDir.mkdirs();
                        }
                        downloadDirectory(file, filePath, auth,basicPath,scanJobId,jobName,jobId,scannerID,path,name,uuId,commonParam);
                    } else {
                        // 如果是文件，下载文件到本地目录
                        downloadFile(file, filePath, auth);
                        //落盘xml文件
                        landingInstructionsFile(file, filePath, basicPath, scanJobId, jobName, jobId, scannerID,path,name,uuId,commonParam);
                    }
                }
            } else {
                // 如果是单个文件，下载文件到本地目录
                String fileName = smbFile.getName();
                String filePath = localPath + fileName;
                downloadFile(smbFile, filePath, auth);
                //落盘xml文件
                landingInstructionsFile(smbFile,filePath,basicPath,scanJobId,jobName,jobId,scannerID, path, name, uuId, commonParam);
            }
            System.out.println("下载完成");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

/*    // 下载文件到本地目录
    private  void downloadFile(SmbFile smbFile, String localPath, NtlmPasswordAuthentication auth)  {
        //String utf8FileName = new String(smbFile.getName().getBytes("GBK"), "UTF-8");
        try (InputStream inputStream = smbFile.getInputStream();
             InputStreamReader isr = new InputStreamReader(inputStream, Charset.forName("GBK"));
             OutputStream outputStream = new FileOutputStream(localPath);
             OutputStreamWriter osw = new OutputStreamWriter(outputStream, Charset.forName("UTF-8"))) {
             //OutputStream outputStream = new FileOutputStream(localPath)) {
            char[] buffer = new char[1024];
            int len;
            while ((len = isr.read(buffer)) != -1) {
                osw.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }*/

    // 下载文件到本地目录
    private  void downloadFile(SmbFile smbFile, String localPath, NtlmPasswordAuthentication auth)  {
        //String utf8FileName = new String(smbFile.getName().getBytes("GBK"), "UTF-8");
        try (InputStream inputStream = smbFile.getInputStream();
             OutputStream outputStream = new FileOutputStream(localPath)) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 下载子目录中的文件和子目录
    private  void downloadDirectory(SmbFile smbDir, String localDir, NtlmPasswordAuthentication auth,
                                    StringBuilder basicPath, String scanJobId, String jobName, String jobId, String scannerID, String path, String name, String uuId, CommonParam commonParam) {
        try {
            SmbFile[] files = smbDir.listFiles();
            for (SmbFile file : files) {
                //String fileName = file.getName();
                String encodedFileName = new String(file.getName().getBytes("UTF-8"), "ISO-8859-1");
                String fileName = new String(encodedFileName.getBytes("ISO-8859-1"), "UTF-8");
                String filePath = localDir + File.separator + fileName;
                String filePaths = localDir + fileName;//测试用的
                log.error("路径乱码情况2的源文件是: " + filePath);
                log.error("路径乱码情况测试的源文件是: " + filePaths);
                if (file.isDirectory()) {
                    File localSubDir = new File(filePath);
                    if (!localSubDir.exists()) {
                        localSubDir.mkdirs();
                    }
                    downloadDirectory(file, filePath, auth, basicPath, scanJobId, jobName, jobId, scannerID, path, name, uuId, commonParam);
                } else {
                    downloadFile(file, filePath, auth);
                    //落盘xml文件
                    landingInstructionsFile(file, filePath, basicPath, scanJobId, jobName, jobId, scannerID, path, name, uuId, commonParam);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void landingInstructionsFile(SmbFile smbFile, String filePath, StringBuilder basicPath, String scanJobId,
                                        String jobName, String jobId, String scannerID, String path, String name,
                                        String uuId, CommonParam commonParam){
        //>>>>>>>>>>>说明XML文件落盘
        Document document = DocumentHelper.createDocument();
        //document.addProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-16LE\"");
        Element rootElem = document.addElement("TaskInfo");
        rootElem.addAttribute("Type","FileServer");
        String[] split = path.split("\\\\");
        String ip = split[2];
        String smbFiles = smbFile.toString().replace("smb:","");
        //ServerInfo
        Element dbInfoElem = DocumentHelper.createElement("ServerInfo");
        dbInfoElem.addAttribute("servertype","SMB");//数据类型
        dbInfoElem.addAttribute("serverip",ip);//网络共享地址
        dbInfoElem.addAttribute("serverPort","445");//端口
        dbInfoElem.addAttribute("credentialname","");//凭据名称
        dbInfoElem.addAttribute("username",name);//登录服务器的用户名
        dbInfoElem.addAttribute("rootname",path);//扫描的根目录名称
        dbInfoElem.addAttribute("devicename","scanner");//扫描设备名称
        dbInfoElem.addAttribute("resguid",uuId);//uuId
        rootElem.add(dbInfoElem);

        //TaskkBaseInfo
        Element taskkBaseInfo = DocumentHelper.createElement("TaskBaseInfo");
        taskkBaseInfo.addAttribute("TaskID",jobId);
        taskkBaseInfo.addAttribute("TaskName",jobName);
        taskkBaseInfo.addAttribute("ScannerID",scannerID);
        rootElem.add(taskkBaseInfo);

        //TaskkProgressInfo
        Element taskkProgressInfo = DocumentHelper.createElement("TaskProgressInfo");
        taskkProgressInfo.addAttribute("sum_file_count","0");//扫描任务总行数
        taskkProgressInfo.addAttribute("cur_file_pos","0");//当前任务节点所在的总行数
        taskkProgressInfo.addAttribute("cur_file_count","0");//
        rootElem.add(taskkProgressInfo);

        //DB_Policy
        Element dbPolicy = DocumentHelper.createElement("DBPolicy");
        dbPolicy.addAttribute("PolicyPath",commonParam.getPolicyPath());
        dbPolicy.addAttribute("FingerPath",commonParam.getFingerPath());
        dbPolicy.addAttribute("DictPath",commonParam.getDictPath());
        dbPolicy.addAttribute("MchClusterPath",commonParam.getMchClusterPath());
        rootElem.add(dbPolicy);

        //FileList
        Element fileList = DocumentHelper.createElement("FileList");
        rootElem.add(fileList);
        //FileItem
        Element fileItem_ = DocumentHelper.createElement("FileItem");
        fileItem_.addAttribute("ServerPath", String.valueOf(smbFiles));
        fileItem_.addAttribute("LocalPath",filePath);
        fileList.add(fileItem_);


        OutputStreamWriter xmlWriter = null;
        StringBuilder xmlPath = new StringBuilder(basicPath);
        String p1 = String.valueOf(Paths.get(path));
        String p2 = String.valueOf(Paths.get(smbFiles));
        String newStr = p2.substring(p1.length() + 1);
        String str = newStr;
        String replace = str.replace("\\", "_");
        log.error("p1的值是:   " + p1 +"p2的值是:   " + p2 + "    拆分出来的字符串是:   "+replace);
        log.error("网络共享扫描的配置文件的落盘地址:\n"+xmlPath);
        xmlPath.append(File.separator).append("config_share_").append(replace).append(".xml");
        log.error("网络共享扫描录制设置之后的配置文件的落盘地址+\n"+xmlPath);
        log.error("程序执行到这里吗?");

        // XMLWriter writer = null;
        try {
            //String xmlStr = ByteStreamHelper.toXmlString(rootElem,StandardCharsets.UTF_8);
            //UTF-16LE，需要手动加入BOM头
            String xmlStr = rootElem.asXML();
            byte[] bom = {-1, -2};

            File file = new File(xmlPath.toString());
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }

            FileOutputStream outputStream = new FileOutputStream(xmlPath.toString());
            outputStream.write(bom);
            log.error("程序执行到这里吗2?");

            xmlWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_16LE);
            BufferedWriter bw = new BufferedWriter(xmlWriter);
            log.error("程序执行到这里吗3?");
            //writer =  new XMLWriter(new FileOutputStream(xmlPath.toString()), (OutputFormat) document);
            bw.write(xmlStr);
            log.error("程序执行到这里吗4?");
            bw.flush();
            bw.close();
        } catch (IOException e) {
            log.error("Failed Method Write xml error: " + e.getMessage());
        }finally {
            if (null != xmlWriter){
                try {
                    xmlWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //调用引擎传输数据
        engineServer.callScannerEngine(jobId, "scanproxy", EngineStatus.DATA.getValue(),xmlPath.toString());
    }


}
